 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  * Michael Williamson (eclipse-bugs@magnaworks.com) - patch (see Bugzilla #92545)
  *
  *******************************************************************************/
 package org.eclipse.ui.forms.widgets;

 import java.util.Hashtable ;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.PaintEvent;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.GC;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.ui.internal.forms.widgets.FormUtil;

 /**
  * A variation of the expandable composite that adds optional description below
  * the title. Section is often used as a basic building block in forms because
  * it provides for logical grouping of information.
  * <p>
  * In case of the TITLE_BAR style, Section renders the title bar in a way
  * compatible with the rest of the workbench. Since it is a widget, all the
  * colors must be supplied directly. When created by the form toolkit, these
  * colors are supplied by the toolkit. The toolkit initializes these colors
  * based on the system colors. For this reason, it is recommended to create the
  * section by the toolkit instead of through its own constructor.
  * <p>
  * Since 3.1, it is possible to set a control to be used for section
  * description. If used, <code>DESCRIPTION</code> style should not be set. A
  * typical way to take advantage of the new method is to set an instance of
  * <code>FormText</code> to provide for hyperlinks and images in the
  * description area.
  *
  * @since 3.0
  */
 public class Section extends ExpandableComposite {
     /**
      * Description style. If used, description will be rendered below the title.
      */
     public static final int DESCRIPTION = 1 << 7;

     private Control descriptionControl;

     private Control separator;

     private Hashtable titleColors;

     private static final String COLOR_BG = "bg"; //$NON-NLS-1$

     private static final String COLOR_GBG = "gbg"; //$NON-NLS-1$

     private static final String COLOR_BORDER = "border"; //$NON-NLS-1$

     /**
      * Creates a new section instance in the provided parent.
      *
      * @param parent
      * the parent composite
      * @param style
      * the style to use
      */
     public Section(Composite parent, int style) {
         this(parent, SWT.NULL, style);
     }

     Section(Composite parent, int cstyle, int style) {
         super(parent, cstyle | getBackgroundStyle(style), style);
         int rtl = cstyle & SWT.RIGHT_TO_LEFT;
         if ((style & DESCRIPTION) != 0) {
             descriptionControl = new Text(this, SWT.READ_ONLY | SWT.WRAP | rtl);
         }
         if ((style & TITLE_BAR) != 0) {
             Listener listener = new Listener() {
                 public void handleEvent(Event e) {
                     Image image = Section.super.getBackgroundImage();
                     if (image != null)
                         image.dispose();
                     Section.super.setBackgroundImage(null);
                 }
             };
             addListener(SWT.Dispose, listener);
             addListener(SWT.Resize, listener);
         }
     }

     private static int getBackgroundStyle(int estyle) {
         return ((estyle & TITLE_BAR) != 0) ? SWT.NO_BACKGROUND : SWT.NULL;
     }

     protected void internalSetExpanded(boolean expanded) {
         super.internalSetExpanded(expanded);
         if ((getExpansionStyle() & TITLE_BAR) != 0) {
             if (!expanded)
                 super.setBackgroundImage(null);
         }
         reflow();
     }

     /**
      * Reflows this section and all the parents up the hierarchy until a
      * ScrolledForm is reached.
      */
     protected void reflow() {
         Composite c = this;
         while (c != null) {
             c.setRedraw(false);
             c = c.getParent();
             if (c instanceof ScrolledForm) {
                 break;
             }
         }
         c = this;
         while (c != null) {
             c.layout(true);
             c = c.getParent();
             if (c instanceof ScrolledForm) {
                 ((ScrolledForm) c).reflow(true);
                 break;
             }
         }
         c = this;
         while (c != null) {
             c.setRedraw(true);
             c = c.getParent();
             if (c instanceof ScrolledForm) {
                 break;
             }
         }
     }

     /**
      * Sets the description text. Has no effect if DESCRIPTION style was not
      * used to create the control.
      *
      * @param description
      */
     public void setDescription(String description) {
         if (descriptionControl instanceof Text)
             ((Text) descriptionControl).setText(description);
     }

     /**
      * Returns the current description text.
      *
      * @return description text or <code>null</code> if DESCRIPTION style was
      * not used to create the control.
      */
     public String getDescription() {
         if (descriptionControl instanceof Text)
             return ((Text) descriptionControl).getText();
         return null;
     }

     /**
      * Sets the separator control of this section. The separator must not be
      * <samp>null </samp> and must be a direct child of this container. If
      * defined, separator will be placed below the title text and will remain
      * visible regardless of the expansion state.
      *
      * @param separator
      * the separator that will be placed below the title text.
      */
     public void setSeparatorControl(Control separator) {
         Assert.isTrue(separator != null && separator.getParent().equals(this));
         this.separator = separator;
     }

     /**
      * Returns the control that is used as a separator betweeen the title and
      * the client, or <samp>null </samp> if not set.
      *
      * @return separator control or <samp>null </samp> if not set.
      */
     public Control getSeparatorControl() {
         return separator;
     }

     /**
      * Sets the background of the section.
      *
      * @param bg
      * the new background
      */
     public void setBackground(Color bg) {
         super.setBackground(bg);
         if (descriptionControl != null
                 && (getExpansionStyle() & DESCRIPTION) != 0)
             descriptionControl.setBackground(bg);
     }

     /**
      * Sets the foreground of the section.
      *
      * @param fg
      * the new foreground.
      */
     public void setForeground(Color fg) {
         super.setForeground(fg);
         if (descriptionControl != null
                 && (getExpansionStyle() & DESCRIPTION) != 0)
             descriptionControl.setForeground(fg);
     }

     /**
      * Returns the control used to render the description. In 3.1, this method
      * was promoted to public.
      *
      * @return description control or <code>null</code> if DESCRIPTION style
      * was not used to create the control and description control was
      * not set by the client.
      * @see #setDescriptionControl(org.eclipse.swt.widgets.Control)
      */
     public Control getDescriptionControl() {
         return descriptionControl;
     }

     /**
      * Sets the description control of this section. The control must not be
      * <samp>null</samp> and must be a direct child of this container. If
      * defined, contol will be placed below the title text and the separator and
      * will be hidden int he collapsed state.
      * <p>
      * This method and <code>DESCRIPTION</code> style are mutually exclusive.
      * Use the method only if you want to create the description control
      * yourself.
      *
      * @since 3.1
      * @param descriptionControl
      * the control that will be placed below the title text.
      */
     public void setDescriptionControl(Control descriptionControl) {
         Assert.isTrue((getExpansionStyle() & DESCRIPTION) == 0);
         Assert.isTrue(descriptionControl != null
                 && descriptionControl.getParent().equals(this));
         this.descriptionControl = descriptionControl;
     }

     /**
      * Sets the color of the title bar border when TITLE_BAR style is used.
      *
      * @param color
      * the title bar border color
      */
     public void setTitleBarBorderColor(Color color) {
         putTitleBarColor(COLOR_BORDER, color);
     }

     /**
      * Sets the color of the title bar background when TITLE_BAR style is used.
      * This color is used as a starting color for the vertical gradient.
      *
      * @param color
      * the title bar border background
      */
     public void setTitleBarBackground(Color color) {
         putTitleBarColor(COLOR_BG, color);
     }

     /**
      * Sets the color of the title bar gradient background when TITLE_BAR style
      * is used. This color is used at the height where title controls end
      * (toggle, tool bar).
      *
      * @param color
      * the title bar gradient background
      */
     public void setTitleBarGradientBackground(Color color) {
         putTitleBarColor(COLOR_GBG, color);
     }

     /**
      * Returns the title bar border color when TITLE_BAR style is used.
      *
      * @return the title bar border color
      */
     public Color getTitleBarBorderColor() {
         if (titleColors == null)
             return null;
         return (Color) titleColors.get(COLOR_BORDER);
     }

     /**
      * Returns the title bar gradient background color when TITLE_BAR style is
      * used.
      *
      * @return the title bar gradient background
      */
     public Color getTitleBarGradientBackground() {
         if (titleColors == null)
             return null;
         if ((getExpansionStyle() & SHORT_TITLE_BAR) != 0)
             return getBackground();
         return (Color) titleColors.get(COLOR_GBG);
     }

     /**
      * Returns the title bar background when TITLE_BAR style is used.
      *
      * @return the title bar background
      */
     public Color getTitleBarBackground() {
         if (titleColors == null)
             return null;
         return (Color) titleColors.get(COLOR_BG);
     }

     private void putTitleBarColor(String key, Color color) {
         if (color == null)
             return;
         if (titleColors == null)
             titleColors = new Hashtable ();
         titleColors.put(key, color);
     }

     protected void onPaint(PaintEvent e) {
         Color bg = null;
         Color fg = null;
         Color border = null;

         GC gc = e.gc;
         Image buffer = null;
         Rectangle bounds = getClientArea();

         if ((getExpansionStyle() & TITLE_BAR) != 0) {
             buffer = new Image(getDisplay(), bounds.width, bounds.height);
             buffer.setBackground(getBackground());
             gc = new GC(buffer);
         }
         if (titleColors != null) {
             bg = (Color) titleColors.get(COLOR_BG);
             fg = getTitleBarForeground();
             border = (Color) titleColors.get(COLOR_BORDER);
         }
         if (bg == null)
             bg = getBackground();
         if (fg == null)
             fg = getForeground();
         if (border == null)
             border = fg;
         int theight = 0;
         int gradientheight = 0;
         int tvmargin = IGAP;
         if ((getExpansionStyle() & TITLE_BAR) != 0) {
             Point tsize = null;
             Point tcsize = null;
             if (toggle != null)
                 tsize = toggle.getSize();
             int twidth = bounds.width - marginWidth - marginWidth;
             if (tsize != null)
                 twidth -= tsize.x + IGAP;
             if (getTextClient() != null)
                 tcsize = getTextClient().getSize();
             if (tcsize != null)
                 twidth -= tcsize.x + IGAP;
             Point size = textLabel.getSize();
             if (tsize != null)
                 theight += Math.max(theight, tsize.y);
             gradientheight = theight;
             if (tcsize != null) {
                 theight = Math.max(theight, tcsize.y);
             }
             theight = Math.max(theight, size.y);
             gradientheight = Math.max(gradientheight, size.y);
             theight += tvmargin + tvmargin;
             gradientheight += tvmargin + tvmargin;
         } else {
             theight = 5;
         }
         if ((getExpansionStyle() & TITLE_BAR) != 0) {
             if (getBackgroundImage() == null)
                 updateHeaderImage(bg, bounds, gradientheight, theight);
             gc.setBackground(getBackground());
             gc.fillRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
             drawBackground(gc, bounds.x, bounds.y, bounds.width, theight);
             if (marginWidth > 0) {
                 // fix up margins
 gc.setBackground(getBackground());
                 gc.fillRectangle(0, 0, marginWidth, theight);
                 gc.fillRectangle(bounds.x + bounds.width - marginWidth, 0,
                         marginWidth, theight);
             }
         } else if (isExpanded()) {
             gc.setForeground(bg);
             gc.setBackground(getBackground());
             gc.fillGradientRectangle(marginWidth, marginHeight, bounds.width
                     - marginWidth - marginWidth, theight, true);
         }
         gc.setBackground(getBackground());
         FormUtil.setAntialias(gc, SWT.ON);
         // repair the upper left corner
 gc.fillPolygon(new int[] { marginWidth, marginHeight, marginWidth,
                 marginHeight + 2, marginWidth + 2, marginHeight });
         // repair the upper right corner
 gc.fillPolygon(new int[] { bounds.width - marginWidth - 3,
                 marginHeight, bounds.width - marginWidth, marginHeight,
                 bounds.width - marginWidth, marginHeight + 3 });
         gc.setForeground(border);
         if (isExpanded() || (getExpansionStyle() & TITLE_BAR) != 0) {
             // top left curve
 gc.drawLine(marginWidth, marginHeight + 2, marginWidth + 2,
                     marginHeight);
             // top edge
 gc.drawLine(marginWidth + 2, marginHeight, bounds.width
                     - marginWidth - 3, marginHeight);
             // top right curve
 gc.drawLine(bounds.width - marginWidth - 3, marginHeight,
                     bounds.width - marginWidth - 1, marginHeight + 2);
         } else {
             // collapsed short title bar
 // top edge
 gc.drawLine(marginWidth, marginHeight, bounds.width - 1,
                     marginHeight);
         }
         if ((getExpansionStyle() & TITLE_BAR) != 0 || isExpanded()) {
             // left vertical edge gradient
 gc.fillGradientRectangle(marginWidth, marginHeight + 2, 1,
                     gradientheight - 2, true);
             // right vertical edge gradient
 gc.fillGradientRectangle(bounds.width - marginWidth - 1,
                     marginHeight + 2, 1, gradientheight - 2, true);
         }
         if ((getExpansionStyle() & TITLE_BAR) != 0) {
             // New in 3.3 - edge treatmant
 gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_WHITE));
             gc.drawPolyline(new int[] { marginWidth + 1,
                     marginHeight + gradientheight - 1, marginWidth + 1,
                     marginHeight + 2, marginWidth + 2, marginHeight + 2,
                     marginWidth + 2, marginHeight + 1,
                     bounds.width - marginWidth - 3, marginHeight + 1,
                     bounds.width - marginWidth - 3, marginHeight + 2,
                     bounds.width - marginWidth - 2, marginHeight + 2,
                     bounds.width - marginWidth - 2,
                     marginHeight + gradientheight - 1 });
         }
         if (buffer != null) {
             gc.dispose();
             e.gc.drawImage(buffer, 0, 0);
             buffer.dispose();
         }
     }

     private void updateHeaderImage(Color bg, Rectangle bounds, int theight,
             int realtheight) {
         Image image = new Image(getDisplay(), 1, realtheight);
         image.setBackground(getBackground());
         GC gc = new GC(image);
         gc.setBackground(getBackground());
         gc.fillRectangle(0, 0, 1, realtheight);
         gc.setForeground(bg);
         gc.setBackground(getBackground());
         gc.fillGradientRectangle(0, marginHeight + 2, 1, theight - 2, true);
         gc.dispose();
         super.setBackgroundImage(image);
     }

     /**
      * Background image is used for the title gradient - does nothing.
      */
     public final void setBackgroundImage(Image image) {
     }
 }

